Skip to content

Move to the Screenplay 2.x packages and report the codes they assign - #66

Merged
woksin merged 4 commits into
mainfrom
chore/screenplay-2.1.0
Aug 10, 2026
Merged

Move to the Screenplay 2.x packages and report the codes they assign#66
woksin merged 4 commits into
mainfrom
chore/screenplay-2.1.0

Conversation

@woksin

@woksin woksin commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Changed

  • screenplay validate reports the diagnostic code the compiler assigns, for example error PLAY0027:, alongside the file and position it already carried
  • Cratis.Screenplay to 2.1.0, Cratis.Arc.Screenplay to 21.2.0, the Cratis.Prologue packages to 1.2.0 and the Cratis.Chronicle packages to 16.19.3 (Upgrade to Arc 21, Chronicle 16.19.2 and Fundamentals 7.17.1 #64)

woksin added 3 commits August 10, 2026 19:31
Cratis.Screenplay 1.6.4 to 2.1.0, with the two packages built against it
moved in the same step: Cratis.Arc.Screenplay to 21.2.0 and the Prologue
packages to 1.2.0. Both embed the compiler's syntax types, which are
positional records, so a version of either built against Screenplay 1.x
resolves against 2.1.0, compiles clean and then throws
MissingMethodException the first time a document is generated.

Arc.Screenplay 21.1.0 is not enough - it still depends on Screenplay
1.5.2; only 21.2.0 is the 2.x rebuild.

Chronicle packages to 16.21.0, which also moves the transitive
Cratis.Arc.MongoDB off 21.0.0 and its unlisted SharpCompress dependency.
Screenplay 2.x assigns every diagnostic a stable PLAY code. The code was
being thrown away and reported as empty, which was accurate against the
1.x compiler and left every diagnostic from 'screenplay validate'
impossible to look up, suppress or match on.
The generator and the compiler it is built against meet only at run
time, so a mismatched pair of packages builds clean and passes every
spec and then throws MissingMethodException the first time anyone runs
'screenplay generate'. Nothing in the suite ran the generator, so that
class of failure could only be found by hand.

Generating is separated from loading so it can be driven from a
compilation built from source. Loading one from disk needs an MSBuild
workspace, which starts a build host process of its own and is neither
quick nor reliable enough to sit in front of this.
@woksin woksin added the minor New features, non-breaking additions label Aug 10, 2026
@woksin

woksin commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

Reviewer context — none of this belongs in the release notes.

Why all three Screenplay-consuming packages move together

Cratis.Arc.Screenplay and Cratis.Prologue.Screenplay are both built against Cratis.Screenplay's syntax types, which are positional records. Screenplay 2.0.0 added parameters to several of them, so the constructors a 1.x-built package calls no longer exist. NuGet still unifies on 2.1.0, the assembly still loads, and it fails only when the code actually runs.

Measured on a deliberately skewed build (Screenplay 2.1.0 with Arc.Screenplay 21.0.1 and Prologue.Screenplay 1.1.0):

dotnet build   -> 0 errors, 0 warnings
dotnet test    -> Passed! 431/431
cratis screenplay generate ...
  Error: Method not found: 'Void Cratis.Screenplay.Syntax.ApplicationSyntax..ctor(...)'
cratis prologue interpret ...
  Error: Method not found: 'Void Cratis.Screenplay.Syntax.ApplicationSyntax..ctor(...)'

Two traps worth knowing about:

  • Cratis.Arc.Screenplay 21.1.0 is not enough. It was published the same day as 21.2.0 and still depends on Cratis.Screenplay [1.5.2, ). Only 21.2.0 is the 2.x rebuild.
  • Cratis.Prologue.Screenplay has the same problem and is easy to miss because nothing about the Screenplay bump names Prologue. 1.1.0 depends on Cratis.Screenplay [1.4.0, ); 1.2.0 is the 2.1.0 rebuild.

The new spec

for_ArcScreenplayGeneration is the guard against this recurring. It builds a compilation from source, generates from it, and asserts the document — which runs the generator through the syntax constructors and the printer. On the skewed build above it fails with the MissingMethodException rather than passing.

Generating is separated from loading (GenerateFrom) so the spec does not need an MSBuild workspace. That is deliberate, and I tried the alternative first: driving the whole path against a project on disk needs a restore, and a restore started from the spec process inherits the MSBuild environment that generating registers and then fails with MSB4236. Working around that left the spec failing roughly 1 run in 5 inside Roslyn's out-of-process build host (RemoteInvocationException: FileNotFoundException). A spec that flakes in CI is worse than the gap, so ScreenplayCompilationLoader stays uncovered.

Two gaps I did not close: the MSBuild loading path above, and the Prologue side of the same diamond — prologue interpret builds its syntax tree inside the command, so there is no seam to drive without a wider refactor. Both were verified by hand instead (below).

Why screenplay validate is untouched

#65 covers it. Short version: CompileIn compiles each file alone, so a reference that crosses a file boundary reports PLAY0166, but that is a warning and never failed the command. CompileFolder fixes it and reports PLAY0172 as an error for a folder holding more than one independent document — which is what the reference documentation says validate <folder> accepts. That is a breaking change to fix cosmetic noise, so it is left out of a minor release.

Verification

dotnet build Cli.slnx -c Debug 0 errors, 0 warnings
dotnet build Cli.slnx -c Release 0 errors, 0 warnings
dotnet test -c Debug -f net10.0 436/436, 3 consecutive runs
dotnet test -c Release -f net10.0 436/436, 3 consecutive runs
Documentation/verify-markdown.sh 33 files, 36 links, all passed
restore clean, no NU1605

Live, against the real assets/demo-store:

$ cratis screenplay generate assets/demo-store/DemoStore.csproj
domain Bookshop
module Bookshop
  feature Bookshop
    slice Automation Bookshop
      event BookAddedToInventory
      ...
$ cratis screenplay validate MyApp.play -o plain

errors (1):
  error PLAY0027: [MyApp.play(5,5)] Invalid slice declaration 'slice Reserving' - expected 'slice <Type> <Name>'
$ cratis prologue interpret ./captures --file Captured.play
{ "systemName": "Books", "modules": 2, "features": 2, "slices": 2 }

The validate line is the user-facing change, and it matches the sample the reference documentation now shows.

On the hazards named in #64

  • IEventSequence.Revise — does not apply. IEventSequence appears nowhere in this repository; the CLI talks to Chronicle through the gRPC contracts (services.EventSequences.GetEventsFromEventSequenceNumber(...)), not the client-side abstraction, so there is nothing here to leave unimplemented.
  • SharpCompress / NU1605 — was genuinely present and is now gone. Cratis.Chronicle.XUnit.Integration 16.19.1 pulled Cratis.Arc.MongoDB 21.0.0, which is the pre-21.0.3 version with the unlisted dependency. 16.21.0 pulls 21.1.0, which declares the listed SharpCompress 0.50.4. Restore is clean.
  • Arc built against the matching Chronicle — coherent, and not something this PR picks. Cratis.Arc.Screenplay has no Chronicle dependency at all (Cratis.Screenplay, Microsoft.CodeAnalysis.CSharp, System.Text.Json, System.Collections.Immutable), so it cannot skew against Chronicle. The full Arc stack enters only through Cratis.Chronicle.XUnit.Integration 16.21.0, which pins Cratis.Arc [21.1.0, ) itself — the pairing is Chronicle's own choice, inherited rather than selected here.

One thing left deliberately behind

Cratis.Chronicle.* 16.22.0 was published at 16:43 today, after the set for this PR was agreed. I have stayed on 16.21.0 rather than silently taking a version that is an hour old and unreviewed. Worth noting that the scheduled Update Packages workflow has failed every day since 2026-08-05, so nothing is picking these up automatically.

Chronicle 16.20.0 flips the default of skipTlsValidation: the client
validated nothing by default and now validates the server certificate
unless the connection string opts out. The CLI's own default server is
chronicle://localhost:35000 with nothing else on it, and a development
server serves a self-signed certificate, so every local connection
starts failing with "The SSL connection could not be established" - 108
of the 161 integration specs.

That is a breaking change for the most common way the CLI is used and
wants deciding on its own terms rather than arriving inside a Screenplay
bump. 16.19.3 is the last version with the old default.
@woksin

woksin commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

Correction to the above — the Chronicle bump landed on 16.19.3, not 16.21.0, and the reason is worth a decision of its own.

Chronicle 16.20.0 flips the TLS default

The first CI run failed 108 of the 161 integration specs with "The SSL connection could not be established". It is not flaky, not the container image, and not this branch's Screenplay work — a workflow_dispatch of Build on unchanged main passed at the same moment, and I reproduced and bisected it locally against a fixed local server image:

Chronicle client Integration specs
16.19.1 161/161 pass
16.19.2 161/161 pass
16.19.3 161/161 pass
16.20.0 108 fail
16.21.0 108 fail
16.22.0 108 fail

Only the client packages matter; Cratis.Chronicle.XUnit.Integration at 16.21.0 with a 16.19.1 client passes, so it is not the harness. Bumping Grpc.Net.Client to 2.83.0 does not help either.

Decompiling Cratis.Chronicle.Connections 16.19.3 against 16.20.0 shows why — the default of skipTlsValidation was inverted:

- /// The client always connects over TLS but does not validate the server's certificate by default,
- /// accepting any certificate, including self-signed ones. Set skipTlsValidation=false to
- /// require full certificate chain validation instead
+ /// The client always connects over TLS and validates the server certificate by default. Set
+ /// skipTlsValidation=true only for an explicitly trusted development server whose
+ /// certificate cannot be validated.

Upstream updated its own ChronicleConnectionString.Development constant to carry ?skipTlsValidation=true in the same release, which is the tell that this is deliberate hardening rather than a regression.

Why that stops this PR taking it

The CLI's default server is chronicle://localhost:35000 (Program.cs, ChronicleSettings.cs, FirstRunDetector.cs) with nothing else on it, and a development Chronicle serves a self-signed certificate. On 16.20.0 and later every out-of-the-box local connection fails until the user adds skipTlsValidation=true by hand. Documentation/reference/connection.md currently states the opposite in two places — that validation is "on by default" and that "the CLI trusts it automatically — no certificate setup is required".

That is a breaking change to the most common way the CLI is used, so it does not belong in a minor release and certainly not as a side effect of a Screenplay bump. 16.19.3 is the last version with the old default, and it keeps every integration spec green.

Worth its own issue and PR, where the real question can be answered: whether the CLI should default skipTlsValidation=true for localhost and preserve today's experience, or take the break, update the connection reference and go out as major. Happy to open that if you want it.

Two other things found on the way

  • A silent package downgrade already on main. Cratis.Chronicle.Connections requires Grpc.Net.Client [2.83.0, ) at both 16.19.1 and 16.21.0, but Directory.Packages.props pins 2.80.0 and Directory.Build.props carries NU1605 in NoWarn, so the downgrade is accepted without a word. Source/Cli resolves 2.80.0 while Integration/Chronicle resolves 2.83.0. It is not the cause of the TLS failure and it predates this branch, so I have left it alone — but suppressing NU1605 repo-wide means the next downgrade is silent too.
  • Cratis.Chronicle.* 16.22.0 was published at 16:43 today. Not taken, for the reason above. Also note the scheduled Update Packages workflow has failed every day since 2026-08-05, so nothing is picking new versions up on its own.

Verification after the change

dotnet build Debug and Release both 0 errors / 0 warnings; unit specs 436/436 in both, three consecutive runs each; integration specs 161/161 against a local Chronicle container; Documentation/verify-markdown.sh passing. screenplay generate, screenplay validate and prologue interpret all re-run live and unchanged by the Chronicle hold.

@woksin
woksin merged commit 26493b4 into main Aug 10, 2026
3 checks passed
@woksin
woksin deleted the chore/screenplay-2.1.0 branch August 10, 2026 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor New features, non-breaking additions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant